#ifndef MCL_LOG_H_
#define MCL_LOG_H_

#include <stdarg.h>

enum loglevel {
	LOG_DEBUG,
	LOG_INFO,
	LOG_ERROR,
	LOG_FATAL,
};

extern enum loglevel LOG_LEVEL_MIN;

void logging_ap(enum loglevel level, char const *fmt, va_list ap);

static inline void set_logging_level_filter(enum loglevel level)
{
	LOG_LEVEL_MIN = level;
}

#define DEFINE_LOG(LEVEL, LOG_SHORT) \
	static inline void log##LOG_SHORT(char *fmt, ...) \
	{ \
		if (LOG_##LEVEL < LOG_LEVEL_MIN) \
			return; \
		va_list ap; \
		va_start(ap, fmt); \
		logging_ap(LOG_##LEVEL, fmt, ap); \
		va_end(ap); \
	}

static inline void logdebug(char *fmt, ...)
{
	if (LOG_DEBUG < LOG_LEVEL_MIN)
		return;

	va_list ap;
	va_start(ap, fmt);
	logging_ap(LOG_DEBUG, fmt, ap);
	va_end(ap);
}

static inline void loginfo(char *fmt, ...)
{
	if (LOG_INFO < LOG_LEVEL_MIN)
		return;
	va_list ap;
	va_start(ap, fmt);
	logging_ap(LOG_INFO, fmt, ap);
	va_end(ap);
}

static inline void logerror(char *fmt, ...)
{
	if (LOG_ERROR < LOG_LEVEL_MIN)
		return;
	va_list ap;
	va_start(ap, fmt);
	logging_ap(LOG_ERROR, fmt, ap);
	va_end(ap);
}

static inline void logfatal(char *fmt, ...)
{
	if (LOG_FATAL < LOG_LEVEL_MIN)
		return;

	va_list ap;
	va_start(ap, fmt);
	logging_ap(LOG_FATAL, fmt, ap);
	va_end(ap);
}

void panic(void);

#define CHECK(P) \
	do { \
		if (P) \
			break; \
		logfatal("check failed: %s", #P); \
	} while (false)

#define CHECK_EQ(L, R) CHECK(L == R)

#endif
